#include "HelloWorldScene.h"
#include "AppMacros.h"
#include "CCDrawingPrimitives.h"
#include "ShapeStyleDef.h"
#include "LightRendererGL.h"
USING_NS_CC;



CCScene* HelloWorld::scene()
{
    // 'scene' is an autorelease object
    CCScene *scene = CCScene::create();
    
    // 'layer' is an autorelease object
    HelloWorld *layer = HelloWorld::create();

    // add layer as a child to scene
    scene->addChild(layer);

    // return the scene
    return scene;
}

CCNode* g_node = NULL;

// on "init" you need to initialize your instance
bool HelloWorld::init()
{
    //////////////////////////////
    // 1. super init first
    if ( !CCLayer::init() )
    {
        return false;
    }
    
    CCSize visibleSize = CCDirector::sharedDirector()->getVisibleSize();
    CCPoint origin = CCDirector::sharedDirector()->getVisibleOrigin();

	//this->setPosition( 200, -100 );
   
	this->scheduleUpdate();

	g_node = CCNode::create();
	g_node->setPosition(64,64);
	this->addChild( g_node );

	

	swfFile.SetRenderer( &renderer );
	swfFile2.SetRenderer( &LightRenderer );

	//swfFile2.SetRenderer( &renderer );
	//swfFile.open( "data/AllShapes.swf" );
	//swfFile.open( "data/childWithChild.swf" );
	//swfFile.open( "data/fillstyletest.swf" );
	//swfFile.open( "data/rotation.swf" );
	//swfFile.open( "data/Shape2.swf" );
	//swfFile.open( "data/Shapes.swf" );
	//swfFile.open( "data/simpleFlashFile.swf" );
	//swfFile.open( "data/bones.swf" );
	//swfFile.open( "data/shape_bones.swf" );
	//swfFile.open( "data/TestBitmap.swf" );
	//swfFile.open( "data/batman.swf" );
	//swfFile.open( "data/TestGrad.swf" );
	//swfFile.open( "data/TestHole.swf" );
	//swfFile.open( "data/TestHole2.swf" );
	//swfFile.open( "data/radial.swf" );
	//swfFile.open( "data/head.swf" );
	//swfFile.open( "data/maskTest.swf" );
	//swfFile.open( "data/TestMask2.swf" );
	//swfFile.open( "data/Edward.swf" );
	//swfFile.open( "data/canabalt.swf" );
	//swfFile.open( "data/astest.swf" );
	//swfFile.open( "data/TestAs2.swf" );
	//swfFile.open( "data/TestAs_3.swf" );
	//swfFile.open( "data/TestAs_4.swf" );
	//swfFile.open( "data/TestText1.swf" );
	//swfFile.open( "data/ninja.swf" );
	swfFile.open( "data/TileMap.swf" );
	swfFile2.open( "data/TestLineAndTris.swf" );

    return true;
}


void HelloWorld::menuCloseCallback(CCObject* pSender)
{
    CCDirector::sharedDirector()->end();

#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
    exit(0);
#endif
}

void HelloWorld::draw( void )
{
	//swfFile.draw();
	swfFile2.draw();
}

void HelloWorld::update(float dt)
{
	swfFile.update( dt );
	swfFile2.update( dt );
}

void Renderer::pushMatrix( const MATRIX& transform ){

	kmGLPushMatrix();

	float x =  transform._13 ;
	float y =  transform._23 ;

	kmMat4 transfrom4x4;

	// | m[0] m[4] m[8]  m[12] |     | a c 0 tx |	  
    // | m[1] m[5] m[9]  m[13] |     | b d 0 ty |	  |_11 _12 _13|		 |a b tx|
    // | m[2] m[6] m[10] m[14] | <=> | 0 0 1  0 | <=> |_21 _22 _23|  <=> |c d ty| 
    // | m[3] m[7] m[11] m[15] |     | 0 0 0  1 |	  
    
	float* m = transfrom4x4.mat;
	float a = transform._11;
	float b = transform._12;
	float tx = transform._13;
	float c = transform._21;
	float d = transform._22;
	float ty = transform._23;

    m[2] = m[3] = m[6] = m[7] = m[8] = m[9] = m[11] = m[14] = 0.0f;
    m[10] = m[15] = 1.0f;
    m[0] = a; m[4] = c; m[12] = tx;
    m[1] = b; m[5] = d; m[13] = ty;


	kmGLMultMatrix( &transfrom4x4 );
}

void Renderer::DrawLine( const vec2& origin, const vec2& destination ){
	ccDrawLine( CCPoint(origin.x,origin.y) , CCPoint(destination.x,destination.y) );
}

void Renderer::DrawQuadBezier( const vec2& origin, const vec2& control, const vec2& destination, uint32 segments ){
	ccDrawQuadBezier( CCPoint(origin.x,origin.y), CCPoint(control.x,control.y) , CCPoint(destination.x,destination.y), segments );
}

void Renderer::DrawPoint( const vec2& point ){
	ccDrawCircle(CCPoint(point.x,point.y), m_PointSize, 0, 10 , false, 1, 1 );
}

void Renderer::popMatrix(){
	kmGLPopMatrix();
}

void Renderer::setCurrentColor( float r, float g, float b, float a ){
	m_Color = ccc4f(r,g,b,a);
	ccDrawColor4F(r,g,b,a);
}

void Renderer::setPointSize( float pointSize ){
	m_PointSize = pointSize;
	ccPointSize(pointSize);
}

#define DEBUG_DRAW_TRIS_EDGE 1
#define MAX_TRIS_BUFFER 1000


void Renderer::DrawSolidTris( const array<vec2>& Tris )
{
	if( Tris.size() < MAX_TRIS_BUFFER ){
		CCPoint vertices[MAX_TRIS_BUFFER];
		int size = 0;
		for (uint32 i=0; i<Tris.size(); i++){
			vertices[i].setPoint( Tris[i].x, Tris[i].y );
			size++;
		}
		ccDrawSolidTris( vertices, size, m_Color);

#if DEBUG_DRAW_TRIS_EDGE
		CCPoint verticesLine[MAX_TRIS_BUFFER*2];
		int sizeLine = 0;
		for (int32 i=0; i<size; i+=3){
			verticesLine[sizeLine] = vertices[i]; sizeLine++; verticesLine[sizeLine] = vertices[i+1]; sizeLine++;
			verticesLine[sizeLine] = vertices[i+1]; sizeLine++; verticesLine[sizeLine] = vertices[i+2]; sizeLine++;
			verticesLine[sizeLine] = vertices[i+2]; sizeLine++; verticesLine[sizeLine] = vertices[i]; sizeLine++;
		}
		setPointSize(2.0f);
		pushCXFORM( CXFORM(0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,1.0f), false );
		ccDrawLines( verticesLine, sizeLine,ccc4f(0.0f,0.0f,0.0f,1.0f) );
		popCXFORM();
		setPointSize(1.0f);
		pushCXFORM( CXFORM(0.0f,0.0f,0.0f,0.0f,1.0f,1.0f,1.0f,1.0f), false );
		ccDrawLines( verticesLine, sizeLine,ccc4f(0.0f,0.0f,0.0f,1.0f) );
		popCXFORM();
#endif
	}
	else {
		assert(0);
	}
}

void Renderer::DrawSolidTrisTextured( const array<vec2>& Tris, const array<vec2>& UVs, uint32 texture ){

	CCTexture2D* pTexture = m_Textures[texture];

	if( Tris.size() < MAX_TRIS_BUFFER && UVs.size() < MAX_TRIS_BUFFER && pTexture ){
		CCPoint vertices[MAX_TRIS_BUFFER];
		CCPoint uvs[MAX_TRIS_BUFFER];
		int size = 0;
		for (uint32 i=0; i<Tris.size(); i++){
			vertices[i].setPoint( Tris[i].x, Tris[i].y );
			size++;
		}
		int size2 = 0;
		for (uint32 i=0; i<UVs.size(); i++){
			uvs[i].setPoint( UVs[i].x, UVs[i].y );
			size2++;
		}

		assert( size == size2 );

		ccDrawSolidTrisTextured( vertices, uvs, size, pTexture );

#if DEBUG_DRAW_TRIS_EDGE
		CCPoint verticesLine[MAX_TRIS_BUFFER*2];
		int sizeLine = 0;
		for (int32 i=0; i<size; i+=3){
			verticesLine[sizeLine] = vertices[i]; sizeLine++; verticesLine[sizeLine] = vertices[i+1]; sizeLine++;
			verticesLine[sizeLine] = vertices[i+1]; sizeLine++; verticesLine[sizeLine] = vertices[i+2]; sizeLine++;
			verticesLine[sizeLine] = vertices[i+2]; sizeLine++; verticesLine[sizeLine] = vertices[i]; sizeLine++;
		}
		setPointSize(2.0f);
		pushCXFORM( CXFORM(0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,1.0f), false );
		ccDrawLines( verticesLine, sizeLine,ccc4f(0.0f,0.0f,0.0f,1.0f) );
		popCXFORM();
		setPointSize(1.0f);
		pushCXFORM( CXFORM(0.0f,0.0f,0.0f,0.0f,1.0f,1.0f,1.0f,1.0f), false );
		ccDrawLines( verticesLine, sizeLine,ccc4f(0.0f,0.0f,0.0f,1.0f) );
		popCXFORM();
#endif
	}
	else {
		assert(0);
	}
}

void Renderer::DrawSolidTrisGradient( const array<vec2>& Tris, const array<vec2>& UVs, const GRADIENT& Gradient ){

	if( Tris.size() < MAX_TRIS_BUFFER && UVs.size() < MAX_TRIS_BUFFER ){
		CCPoint vertices[MAX_TRIS_BUFFER];
		CCPoint uvs[MAX_TRIS_BUFFER];
		int size = 0;
		for (uint32 i=0; i<Tris.size(); i++){
			vertices[i].setPoint( Tris[i].x, Tris[i].y );
			size++;
		}
		int size2 = 0;
		for (uint32 i=0; i<UVs.size(); i++){
			uvs[i].setPoint( UVs[i].x, UVs[i].y );
			size2++;
		}

		assert( size == size2 );

		ccDrawSolidTrisGradient( vertices, uvs, size, Gradient.getColorControl(), Gradient.getRatioControl(), ! Gradient.m_isLinear );

#if DEBUG_DRAW_TRIS_EDGE
		CCPoint verticesLine[MAX_TRIS_BUFFER*2];
		int sizeLine = 0;
		for (int32 i=0; i<size; i+=3){
			verticesLine[sizeLine] = vertices[i]; sizeLine++; verticesLine[sizeLine] = vertices[i+1]; sizeLine++;
			verticesLine[sizeLine] = vertices[i+1]; sizeLine++; verticesLine[sizeLine] = vertices[i+2]; sizeLine++;
			verticesLine[sizeLine] = vertices[i+2]; sizeLine++; verticesLine[sizeLine] = vertices[i]; sizeLine++;
		}
		setPointSize(2.0f);
		pushCXFORM( CXFORM(0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,0.0f,1.0f), false );
		ccDrawLines( verticesLine, sizeLine,ccc4f(0.0f,0.0f,0.0f,1.0f) );
		popCXFORM();
		setPointSize(1.0f);
		pushCXFORM( CXFORM(0.0f,0.0f,0.0f,0.0f,1.0f,1.0f,1.0f,1.0f), false );
		ccDrawLines( verticesLine, sizeLine,ccc4f(0.0f,0.0f,0.0f,1.0f) );
		popCXFORM();
#endif
	}
	else {
		assert(0);
	}

}

Renderer::Renderer(){
	m_Color = ccc4f(1.0f,1.0f,1.0f,1.0f);
	m_PointSize = 0;
	
	m_Textures.push_back(NULL);
	m_MaskStack = 0;
}

uint32 Renderer::createTextureFromRowData( uint8* _rowData, uint32 width, uint32 height ){

	CCImage image;                
	if (! image.initWithImageData((void*)_rowData, width*height*4 , CCImage::kFmtRawData, width, height ) ){
		assert(0); // error
	}

	CCTexture2D *pTexture = CCTextureCache::sharedTextureCache()->addUIImage( &image, NULL );
	if(pTexture){
		int index = m_Textures.size();
		m_Textures.push_back( pTexture );

		return index;
	}

	return 0;
}



void Renderer::PushMask_Start(){
	
	if(m_MaskStack == 0){
		glClearStencil(0);
		glClearDepth(0);
		glClear(GL_STENCIL_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
		glEnable(GL_STENCIL_TEST);
		glEnable(GL_DEPTH_TEST);
	}
	
	glStencilFunc(GL_EQUAL,m_MaskStack,-1);
	glStencilOp(GL_KEEP,GL_INCR,GL_INCR);

	glDepthFunc(GL_NEVER);
}

void Renderer::PushMask_End(){

	m_MaskStack++;
	glStencilFunc(GL_EQUAL,m_MaskStack,-1);
	glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP);

	glDepthFunc(GL_ALWAYS);
}

void Renderer::PopMask_Start(){

	glStencilFunc(GL_EQUAL,m_MaskStack,-1);
	glStencilOp(GL_KEEP,GL_DECR,GL_DECR);

	glDepthFunc(GL_NEVER);
}

void Renderer::PopMask_End(){

	m_MaskStack--;
	glStencilFunc(GL_EQUAL,m_MaskStack,-1);
	glStencilOp(GL_KEEP,GL_KEEP,GL_KEEP);

	glDepthFunc(GL_ALWAYS);

	if(m_MaskStack == 0){
		glDisable(GL_STENCIL_TEST);
		glDisable(GL_DEPTH_TEST);
	}
}

void Renderer::pushCXFORM( const CXFORM& new_cxform, bool use_stack_transform ){
	if(m_CXFormStack.size() > 0){
		m_CXFormStack.push_back( use_stack_transform ? (new_cxform * m_CXFormStack.back()) : new_cxform );
	}
	else{
		m_CXFormStack.push_back( new_cxform );
	}

	CXFORM& ref = m_CXFormStack.back();
	ccDrawSetCurrentCxForm( true, 
		ccc4f(ref._MultR,ref._MultG,ref._MultB,ref._MultA),
		ccc4f(ref._AddR,ref._AddG,ref._AddB,ref._AddA) );
}

void Renderer::popCXFORM(){
	m_CXFormStack.pop_back();

	if(m_CXFormStack.size() > 0 ){
		CXFORM& ref = m_CXFormStack.back();
		ccDrawSetCurrentCxForm( true, 
			ccc4f(ref._MultR,ref._MultG,ref._MultB,ref._MultA),
			ccc4f(ref._AddR,ref._AddG,ref._AddB,ref._AddA) );
	}
	else{
		ccDrawSetCurrentCxForm( false, ccc4f(1,1,1,1), ccc4f(0,0,0,0) ); 
	}
}
